package cn.jiedanba.itext5.check;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.X509Certificate;

import org.apache.commons.io.IOUtils;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.CertificateID;
import org.bouncycastle.cert.ocsp.CertificateStatus;
import org.bouncycastle.cert.ocsp.OCSPReq;
import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;

/**
 * 使用OCSP查询证书状态
 */
public class OCSPCheck {

	private X509Certificate cert;

	public OCSPCheck() {

	}

	public OCSPCheck(X509Certificate cert) {
		this.cert = cert;
	}

	/**
	 * 检查证书是否有效
	 * 
	 * @return true：有效 false：无效
	 */
	public boolean checkCertStatus() {
		try {
			String ocspUrl = getOcspUrl(cert);

			if (ocspUrl == null) {
				throw new IllegalArgumentException("证书无OCSP查询地址");
			}
			DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().build();
			CertificateID certId = new CertificateID(digCalcProv.get(CertificateID.HASH_SHA1),
					new JcaX509CertificateHolder(cert), cert.getSerialNumber());
			OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
			ocspReqBuilder.addRequest(certId);

			// 生成OCSP请求
			OCSPReq ocspReq = ocspReqBuilder.build();

			byte[] reqByte = ocspReq.getEncoded();
			URL url = new URL(ocspUrl);
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
			connection.setRequestProperty("Content-Type", "application/ocsp-request");
			connection.setRequestProperty("Accept", "application/ocsp-response");
			connection.setDoOutput(true);
			connection.setConnectTimeout(5000);
			connection.setReadTimeout(5000);
			connection.setDoOutput(true);
			OutputStream out = connection.getOutputStream();
			DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out));
			dataOut.write(reqByte);
			dataOut.flush();
			dataOut.close();
			InputStream in = (InputStream) connection.getContent();
			// 获取到结果
			byte[] response = IOUtils.toByteArray(in);

			// 解码OCSP响应
			OCSPResp ocspResp = new OCSPResp(response);
			BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResp.getResponseObject();

			CertificateStatus certStatus = basicResponse.getResponses()[0].getCertStatus();
			if (certStatus == CertificateStatus.GOOD) {
				return true;
			} else {
				return false;
			}
		} catch (Exception e) {
			throw new RuntimeException("OCSP检查出错", e);
		}
	}

	/**
	 * 从证书中获取OCSP URL
	 * 
	 * @param certificate
	 * @return
	 * @throws IOException
	 */
	private String getOcspUrl(X509Certificate certificate) throws IOException {
		try {
			// 从证书的扩展字段中获取OCSP URL
			byte[] extensionValue = certificate.getExtensionValue(Extension.authorityInfoAccess.getId());
			if (extensionValue == null) {
				return null;
			}
			String ocspUrl = null;
			ASN1InputStream asn1InputStream = null;
			try {
				// 解码扩展字段
				asn1InputStream = new ASN1InputStream(extensionValue);
				ASN1OctetString octetString = (ASN1OctetString) asn1InputStream.readObject();

				// 解码扩展字段的内容
				ASN1InputStream extValueInputStream = new ASN1InputStream(octetString.getOctets());
				ASN1Sequence extSequence = (ASN1Sequence) extValueInputStream.readObject();

				// 查找OCSP URL
				for (int i = 0; i < extSequence.size(); i++) {
					ASN1Sequence seq = (ASN1Sequence) extSequence.getObjectAt(i);
					ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier) seq.getObjectAt(0);
					if (oid.getId().equals("1.3.6.1.5.5.7.48.1")) {
						AccessDescription accessDescription = AccessDescription.getInstance(extSequence.getObjectAt(0));
						if (accessDescription.getAccessLocation().getTagNo() == GeneralName.uniformResourceIdentifier) {
							ocspUrl = DERIA5String.getInstance(accessDescription.getAccessLocation().getName())
									.getString();
							break;
						}
					}
				}

			} finally {
				if (asn1InputStream != null) {
					asn1InputStream.close();
				}
			}
			return ocspUrl;
		} catch (Exception e) {
			throw new RuntimeException("从证书中获取OCSP URL出错", e);
		}

	}
}
